#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/init.h>
#include <asm/io.h> 
#include <mach/platform.h>
#include <linux/gpio.h>
#include <linux/delay.h>



static const int pinRS = 7;
static const int pinE = 8;
//jak cos sprobuj z high
static struct gpio pinsDB[] = {
		{25, GPIOF_OUT_INIT_LOW, "DB1"},
		{24, GPIOF_OUT_INIT_LOW, "DB2"},
		{23, GPIOF_OUT_INIT_LOW, "DB2"},
		{18, GPIOF_OUT_INIT_LOW, "DB4"}
};



static dev_t first; // Global variable for the first device number
static struct cdev c_dev; // Global variable for the character device structure
static struct class *cl; // Global variable for the device class

static void cmd(int bits, bool char_mode)
{
	int n, c, k, i, j ,d;
	int bin[8];	
	n = bits;
	printk(KERN_DEBUG "    Number %d\n", bits);
 	for (c = 7; c >= 0; c--)
  	{
    		k = n >> c;
 
	    if (k & 1){      
	      bin[7-c] = 1;
	      
	    }
	    else{
	      bin[7-c] = 0;
	    }
  	}
	printk(KERN_DEBUG "    Pin RS: %d\n", char_mode);
	gpio_set_value(pinRS, char_mode);
		
	for(i=0;i<4;i++)
	{
		gpio_set_value(pinsDB[i].gpio, 0);
		printk(KERN_DEBUG "        Pin %d N: %d is set to 0\n", pinsDB[i].gpio, i);
 
	}

	for(j=0;j<4;j++)
	{
		if(bin[j] == 1)
		{
			gpio_set_value(pinsDB[3-j].gpio, 1);
			printk(KERN_DEBUG "        Pin %d N: %d is set to 1\n", pinsDB[3-j].gpio, j);
		}
	}

	gpio_set_value(pinE, 1);
	gpio_set_value(pinE, 0);
	
	for(i=0;i<4;i++)
        {
	
                gpio_set_value(pinsDB[i].gpio, 0);
         	printk(KERN_DEBUG "        Pin %d N: %d is set to 0\n", pinsDB[i].gpio, i);

                 
        }

	for(d=4;d<8;d++)
	{
		if(bin[d] == 1)
		{
			gpio_set_value(pinsDB[7-d].gpio, 1);
			printk(KERN_DEBUG "Pin %d N: %d is set to 1\n", pinsDB[7-d].gpio, d);
		}
	}
	gpio_set_value(pinE, 1);
	gpio_set_value(pinE, 0);
	printk(KERN_DEBUG "End CMD.............................");

}

static void clear(void)
{
	cmd(51, 0);
	cmd(50, 0);
	cmd(40, 0);
	cmd(12, 0);
	cmd(6, 0);
	cmd(1, 0);
}




static int my_open(struct inode *i, struct file *f)
{
  printk(KERN_INFO "Driver: open()\n");	
  return 0;
}
  static int my_close(struct inode *i, struct file *f)
{
  printk(KERN_INFO "Driver: close()\n");
  return 0;
}
  static ssize_t my_read(struct file *f, char __user *buf, size_t
  len, loff_t *off)
{
  printk(KERN_INFO "Driver: read()\n");
  return 0;
}
  static ssize_t my_write(struct file *f, const char __user *buf,
  size_t len, loff_t *off)
{	
	int i = 0;
	
	printk(KERN_INFO "Driver: write()\n");
	msleep(10);
	if(len > 1 && buf[0] == 61)
	{
		
		if(buf[1] == 'n')
		{
			cmd(192, 0);
		} else if(buf[1] == 'c')
		{
			clear();
		}
			
	} else {

		for(i = 0; i< len-1;i++)
		{
			
			cmd(buf[i], 1);	
			msleep(10);
		}
	
	}	     	
	
	return len;
}
  static struct file_operations pugs_fops =
{
  .owner = THIS_MODULE,
  .open = my_open,
  .release = my_close,
  .read = my_read,
  .write = my_write
};
 
static int __init ofcd_init(void) /* Constructor */
{
	int ret = 0;

  if (alloc_chrdev_region(&first, 0, 1, "Maniek") < 0)
  {
    return -1;
  }
    if ((cl = class_create(THIS_MODULE, "charDisplay")) == NULL)
  {
    unregister_chrdev_region(first, 1);
    return -1;
  }
    if (device_create(cl, NULL, first, NULL, "displayDriver") == NULL)
  {
    class_destroy(cl);
    unregister_chrdev_region(first, 1);
    return -1;
  }
    cdev_init(&c_dev, &pugs_fops);
    if (cdev_add(&c_dev, first, 1) == -1)
  {
    device_destroy(cl, first);
    class_destroy(cl);
    unregister_chrdev_region(first, 1);
    return -1;
  }
 	

	ret = gpio_request_one(pinE, GPIOF_OUT_INIT_LOW, "pinE");
printk(KERN_INFO "pineE: %d", ret);

	ret = gpio_request_one(pinRS, GPIOF_OUT_INIT_LOW, "pinRS");
printk(KERN_INFO "PinRS: %d", ret);

	ret = gpio_request_array(pinsDB, ARRAY_SIZE(pinsDB));
printk(KERN_INFO "pinsDB: %d", ret);

	if (ret) {
		printk(KERN_ERR "Unable to request GPIOs: %d\n", ret);
		return ret;
	}
	printk(KERN_INFO "clear display");
	clear();
	msleep(10);
	cmd('W', 1);
	msleep(10);
	cmd('i', 1);
	msleep(10);
	cmd('t', 1);
	msleep(10);
	cmd('a', 1);
	msleep(10);
	cmd('m', 1);
	msleep(10);
	
	
	  
           

  return 0;
}


 
static void __exit ofcd_exit(void) /* Destructor */
{
	
	int i =0;
	//set values to 0
	gpio_set_value(pinE, 0);
	gpio_set_value(pinRS, 0 );
	clear();
	
	for(i = 0; i < ARRAY_SIZE(pinsDB); i++) {
		gpio_set_value(pinsDB[i].gpio, 0);
	}
	// unregister all GPIOs
	gpio_free_array(pinsDB, ARRAY_SIZE(pinsDB));
	gpio_free(pinE);
	gpio_free(pinRS);

	//other
	cdev_del(&c_dev);
	device_destroy(cl, first);
	class_destroy(cl);
	unregister_chrdev_region(first, 1);
	printk(KERN_INFO "Alvida: ofcd unregistered");
}
 
module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Our First Character Driver");